﻿using System;
using System.Collections.Generic;



namespace ZoeEngine.Framework
{
    public class EntityCollection : ICollection<Entity>
    {
        private bool _isReadOnly = false;
        private List<Entity> _allEntities = new List<Entity>();
        private Dictionary<string, Entity> _namedEntities = new Dictionary<string, Entity>();

        /// <summary>
        /// Finds the entity in the collection with the given name.
        /// </summary>
        /// <param name="name">Entity name to search for.</param>
        /// <returns>Reference to the entity with name.</returns>
        public Entity Find(string name)
        {
            if (_namedEntities.ContainsKey(name))
            {
                return _namedEntities[name];
            }
            return null;
        }
        /// <summary>
        /// Finds the first entity in the collection containing a component of the specified type.
        /// </summary>
        /// <typeparam name="T">Component type to search for.</typeparam>
        /// <returns>Reference to the entity containing the specified type of component.</returns>
        public Entity FindWith<T>() where T : EntityComponent
        {
            foreach (Entity entity in _allEntities)
            {
                if (entity.Contains(typeof(T)))
                {
                    return entity;
                }
            }
            return null;
        }
        /// <summary>
        /// Finds the first entity in the collection with the specified name containing a component of the specified type.
        /// </summary>
        /// <typeparam name="T">Component type to search for.</typeparam>
        /// <param name="name">Entity name to search for.</param>
        /// <returns>Reference to the entity with the specified name containing the specified type of component.</returns>
        public Entity FindWith<T>(string name) where T : EntityComponent
        {
            if (_namedEntities.ContainsKey(name))
            {
                if (_namedEntities[name].Contains(typeof(T)))
                    return _namedEntities[name];
            }
            return null;
        }
        /// <summary>
        /// Finds all components in the collection containing the specified type of component.
        /// </summary>
        /// <typeparam name="T">Component type to search for.</typeparam>
        /// <returns>List of entities containing the specified type of component.</returns>
        public List<Entity> FindAllWith<T>() where T : EntityComponent
        {
            List<Entity> hits = new List<Entity>();
            foreach (Entity entity in _allEntities)
            {
                if (entity.Contains(typeof(T)))
                {
                    hits.Add(entity);
                }
            }
            return hits;
        }
        /// <summary>
        /// Removes an entity from the collection by name.
        /// </summary>
        /// <param name="name">Name of the entity we wish to remove.</param>
        /// <returns>Whether or not the entity was successfully removed.</returns>
        public bool Remove(string name)
        {
            return Remove(Find(name));
        }
        /// <summary>
        /// Remove entity from collection and prioritize the removal of its components.
        /// </summary>
        /// <param name="item">Entity to remove.</param>
        /// <returns>Whether or not the entity was successfully removed from the collection.</returns>
        public virtual bool RemoveImmediate(Entity item)
        {
            return Remove(item);
        }

        /// <summary>
        /// Fired when an entity is added to the collection.
        /// </summary>
        public event EventHandler<EntityEventArgs> EntityAdded;
        protected virtual void OnEntityAdded(EntityEventArgs e)
        {
            if (EntityAdded != null)
            {
                EntityAdded(this, e);
            }
        }

        /// <summary>
        /// Adds an entity to this collection.
        /// </summary>
        /// <param name="item">Entity to add.</param>
        public void Add(Entity item)
        {
            if (item.Manager == null)
            {
                throw new ArgumentException("Cannot add an unmanaged entity to this collection.");
            }

            if (_allEntities.Contains(item))
            {
                throw new ArgumentException("Cannot add a duplicate entity to this collection.");
            }

            _allEntities.Add(item);

            if (item.Name != null && !_namedEntities.ContainsKey(item.Name))
            {
                _namedEntities.Add(item.Name, item);
            }

            OnEntityAdded(new EntityEventArgs(item));
        }
        /// <summary>
        /// Clears the collection.
        /// </summary>
        public virtual void Clear()
        {
            throw new NotImplementedException();
        }
        /// <summary>
        /// Determines if the collection contains the specified entity.
        /// </summary>
        /// <param name="item">Entity to check for.</param>
        /// <returns>Whether or not the collection contains the specified entity.</returns>
        public bool Contains(Entity item)
        {
            return _allEntities.Contains(item);
        }
        /// <summary>
        /// Copies the entities in the collection to an array.
        /// </summary>
        /// <param name="array">Destination array.</param>
        /// <param name="arrayIndex">Index in the array to start copying to.</param>
        public void CopyTo(Entity[] array, int arrayIndex)
        {
            _allEntities.CopyTo(array, arrayIndex);
        }
        /// <summary>
        /// Removes an entity from the collection.
        /// </summary>
        /// <param name="item">Entity to remove.</param>
        /// <returns>Whether or not the entity was successfully removed from the collection.</returns>
        public virtual bool Remove(Entity item)
        {
            if (item == null)
                return false;

            if (_namedEntities.ContainsValue(item))
            {
                _namedEntities.Remove(item.Name);
            }

            if (_allEntities.Contains(item))
            {
                _allEntities.Remove(item);
                return true;
            }

            return false;   
        }
        /// <summary>
        /// Gets an enumerator for this collection.
        /// </summary>
        /// <returns>Full-collection enumerator.</returns>
        public IEnumerator<Entity> GetEnumerator()
        {
            return _allEntities.GetEnumerator();
        }
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return _allEntities.GetEnumerator();
        }

        /// <summary>
        /// Number of items in the collection.
        /// </summary>
        public int Count
        {
            get
            {
                return _allEntities.Count;
            }
        }
        /// <summary>
        /// Whether or not the collection is read-only.
        /// </summary>
        public bool IsReadOnly
        {
            get
            {
                return _isReadOnly;
            }
        }
    }
}
